﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Resources;
using System.Text;
using System.Xml.Serialization;
using NET.Tools;
using Puzzler.Plugin.Interface;
using Puzzler.Plugin.Interface.Attributes;
using Puzzler.Plugin.Interface.Exceptions;
using Puzzler.XML;

namespace Puzzler.Plugin
{
    public struct PuzzleInfo
    {
        private String name, description, note;
        private String iconUri;
        private PuzzleCategory category;

        internal PuzzleInfo(string name, string description, string note, string iconUri, PuzzleCategory category)
        {
            this.name = name;
            this.description = description;
            this.note = note;
            this.iconUri = iconUri;
            this.category = category;
        }

        public string Name
        {
            get { return name; }
        }

        public string Description
        {
            get { return description; }
        }

        public string Note
        {
            get { return note; }
        }

        public string IconUri
        {
            get { return iconUri; }
        }

        public PuzzleCategory Category
        {
            get { return category; }
        }

        public bool Equals(PuzzleInfo other)
        {
            return string.Equals(name, other.name) && string.Equals(description, other.description) && string.Equals(note, other.note) && string.Equals(iconUri, other.iconUri) && category.Equals(other.category);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            return obj is PuzzleInfo && Equals((PuzzleInfo) obj);
        }

        public override int GetHashCode()
        {
            unchecked
            {
                int hashCode = (name != null ? name.GetHashCode() : 0);
                hashCode = (hashCode*397) ^ (description != null ? description.GetHashCode() : 0);
                hashCode = (hashCode*397) ^ (note != null ? note.GetHashCode() : 0);
                hashCode = (hashCode*397) ^ (iconUri != null ? iconUri.GetHashCode() : 0);
                hashCode = (hashCode*397) ^ category.GetHashCode();
                return hashCode;
            }
        }

        public override string ToString()
        {
            return string.Format("Name: {0}, Description: {1}, Note: {2}, Category: {3}, IconUri: {4}", name, description, note, category, iconUri);
        }
    }

    internal static class PuzzleUtils
    {
        private static readonly IDictionary<String, ResourceManager> managers =
            new Dictionary<string, ResourceManager>();

        public static XWizard GetPuzzleWizard(Type type)
        {
            string xmlName = type.FullName + ".wizard.xml";
            using (Stream stream = type.Assembly.GetManifestResourceStream(xmlName))
            {
                if (stream == null)
                    throw new IOException("Cannot find xml: " + xmlName);

                XmlSerializer serializer = new XmlSerializer(typeof(XWizard));
                return serializer.Deserialize(stream) as XWizard;
            }
        }

        public static XRibbonExtension GetPuzzleRibbonExtension(Type type)
        {
            string xmlName = type.FullName + ".ribbon.xml";
            using (Stream stream = type.Assembly.GetManifestResourceStream(xmlName))
            {
                if (stream == null)
                    throw new IOException("Cannot find xml: " + xmlName);

                XmlSerializer serializer = new XmlSerializer(typeof(XRibbonExtension));
                return serializer.Deserialize(stream) as XRibbonExtension;
            }
        }

        public static PuzzleInfo GetPuzzleInfo(Type type)
        {
            PuzzlePluginAttribute attr = ExtractPuzzleInfo(type);
            LanguageResourceAttribute lanAttr = ExtractPuzzleInfoLanguage(type);

            ResourceManager rm;
            if (managers.ContainsKey(lanAttr.LanguageResourceName))
            {
                rm = managers[lanAttr.LanguageResourceName];
            }
            else
            {
                rm = new ResourceManager(lanAttr.LanguageResourceName, type.Assembly);
                managers.Add(lanAttr.LanguageResourceName, rm);
            }

            return new PuzzleInfo(
                ExtractString(attr.Name, rm),
                ExtractString(attr.Description, rm),
                ExtractString(attr.Note, rm),
                attr.IconUri,
                attr.Category
                );
        }

        private static PuzzlePluginAttribute ExtractPuzzleInfo(Type type)
        {
            object[] attributes = type.GetCustomAttributes(typeof(PuzzlePluginAttribute), false);
            if (attributes.Length <= 0)
                throw new PuzzlerPluginException("Cannot find puzzle info attribute on plugin class: " +
                                                 type);

            return (PuzzlePluginAttribute)attributes[0];
        }

        private static LanguageResourceAttribute ExtractPuzzleInfoLanguage(Type type)
        {
            object[] attributes = type.Assembly.GetCustomAttributes(typeof(LanguageResourceAttribute), false);
            if (attributes.Length <= 0)
                return null;

            return (LanguageResourceAttribute)attributes[0];
        }

        private static String ExtractString(String text, ResourceManager rm)
        {
            if (rm == null)
                throw new ArgumentException("Resource Manager is null!");

            string str = rm.GetString(text, CultureInfo.CurrentUICulture);
            if (String.IsNullOrEmpty(str))
            {
                return text;
            }

            return str;
        }
    }
}
